#include <memory>
#include <vector>
#include <functional>
#include <memory>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <unistd.h>
using namespace std;

// 一个定时任务
class TimerTask
{
public:
    using TaskFunc = std::function<void()>;
    using RemoveFunc = std::function<void()>;
    TimerTask(int id, int timeout, TaskFunc func) : _id(id), _taskfunc(func), _timeout(timeout), _cancel(false) {}
    ~TimerTask()
    {
        // 当我们的定时任务生命周期结束的时候执行我们的任务函数
        if (_cancel == false)
            _taskfunc();
        // 当我们的定时任务被执行后 我们要移除我们的定时器在容器中的管理

        // 无论定时任务是否被取消 在定时任务析构的时候我们都要从容器中移除对改定时任务的管理
        _remove(); // 移除我们在容器中的管理
    }
    void Cancel()
    {
        _cancel = true;
    }
    void RegisterRemoveFunc(RemoveFunc func)
    {
        _remove = func;
    }
    int GetTimeout()
    {
        return _timeout;
    }

private:
    int _id;            // 每个定时任务的编号
    TaskFunc _taskfunc; // 要执行的我们的任务函数
    RemoveFunc _remove;
    int _timeout; // 超时时间
    bool _cancel; // 定时任务是否被取消
};
using TaskSharedPtr = std::shared_ptr<TimerTask>;
using TaskWeakptr = std::weak_ptr<TimerTask>;
class TimerWheel
{
public:
    TimerWheel() : _tick(0), _capacity(60), _wheel(_capacity)
    {
    }
    void TimerTaskAdd(int id, int timeout, TaskFunc func)
    {
        // 构建一个shared_ptr
        TaskSharedPtr task = std::make_shared<TimerTask>(id, timeout, func);
        // 注册回调定时任务执行会自动把自己从容器中移除
        task->RegisterRemoveFunc(std::bind(&TimerWheel::RemoveTimer, this, id));
        int pos = task->GetTimeout(); // 获取定时任务的超时时间
        pos = (_tick + pos) % _capacity;
        _wheel[pos].push_back(task);     // 加入轮子对应位置
        _timers[id] = TaskWeakptr(task); // 变为我们的weak指针不会增加引用计数
        // 这里就是如果我们用sharedptr 有两种情况
        // 1. 我们直接用此时的task加入容器中 此时就会导致task引用加1
        // 这样的话 就算轮子上的shared——ptr都释放了 但是容器中还有一个引用计数导致 定时任务无法执行
        // 2.如果我们用源对象重新搞一个shared——ptr 这样虽然我们的task此时的引用计数没有增加 但是如果task资源释放了
        // 此时我们容器中管理的定时任务为空了 我们去移除的时候会报错
        // weakptr 就相当于我们的一个资源的观察者
    }
    void CancelTask(int id)
    {
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            return;
        }

        // 找到之后取消定时任务
        TaskSharedPtr tem = _timers[id].lock(); // 把观察的资源重新生成一个sharedptr 此时如果没有资源指向了就会返回空
        // 反之观察的资源的引用计数增加 但是这里不影响 因为我们tem的出作用域后就没了 不影响时间轮事件的引用计数
        if (tem)
        {
            tem->Cancel(); // 取消定时任务
        }
    }
    // 刷新定时任务
    void Refresh(int id)
    {
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            return;
        }
        // 找到之后取消定时任务
        TaskSharedPtr tem = _timers[id].lock(); // 把观察的资源重新生成一个sharedptr 此时如果没有资源指向了就会返回空
        // 反之观察的资源的引用计数增加 但是这里不影响 因为我们tem的出作用域后就没了 不影响时间轮事件的引用计数
        if (tem)
        {
            int timeout = tem->GetTimeout();
            // 重新设置时间轮
            _wheel[(_tick + timeout) % _capacity].push_back(tem);
        }
    }
    void RunOneOneSecond()
    {
        for (auto &task : _wheels[_tick])
        {
            std::cout << "清理任务，id=" << task->_id << ", use_count=" << task.use_count() << std::endl;
        }
        // 该函数每1s执行一次
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear(); // 此时如果定时任务对象计数为0 就会执行
    }

private:
    void RemoveTimer(int id)
    {
        auto pos = _timers.find(id);
        if (pos == _timers.end())
        {
            return;
        }
        _timers.erase(id);
    }
    int _tick;                                    // 我们的秒级指针 指向哪里就执行
    int _capacity;                                // 轮子容量
    vector<vector<TaskSharedPtr>> _wheel;         // 我们的时间轮子
    std::unordered_map<int, TaskWeakptr> _timers; // 我们的定时时间管理容器 这里用我们的weak是为了防止定时任务的计数最小就是1导致无法触发定时任务的析构函数
};

int main()
{
    TimerWheel wheel;
    wheel.TimerTaskAdd(88, 5, []()
                       { std::cout << "定时任务执行了" << std::endl; });

    // for(int i=0;i<5;i++)
    // {
    //     sleep(1);
    //     wheel.RunOneOneSecond();
    //     wheel.Refresh(88);
    // }
    // while(true)
    // {
    //     sleep(1);
    //     std::cout<<"---------------------"<<std::endl;
    //     wheel.RunOneOneSecond();
    // }

    while (true)
    {
        std::cout << "-----------------------" << std::endl;
        sleep(1);
        wheel.RunOneOneSecond();
        // wheel.Refresh(88);
    }
    return 0;
}